using System;
using System.Collections.Generic;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.transaction
{


	using TransactionManager = javax.transaction.TransactionManager;

///
/// <summary> * The LockManager can lock resources for reading or writing. By doing this one
/// * may achieve different transaction isolation levels. A resource can for now be
/// * any object (but null).
/// * <p>
/// * When acquiring a lock you have to release it. Failure to do so will result in
/// * the resource being blocked to all other transactions. Put all locks in a try -
/// * finally block.
/// * <p>
/// * Multiple locks on the same resource held by the same transaction requires the
/// * transaction to invoke the release lock method multiple times. If a tx has
/// * invoked <CODE>getReadLock</CODE> on the same resource x times in a row it
/// * must invoke <CODE>releaseReadLock</CODE> x times to release all the locks.
/// * <p>
/// * LockManager just maps locks to resources and they do all the hard work
/// * together with a resource allocation graph. </summary>
/// 
	public class LockManager
	{
		private readonly Map<object, RWLock> resourceLockMap = new Dictionary<object, RWLock>();

		private readonly RagManager ragManager;

		public LockManager(TransactionManager tm)
		{
			ragManager = new RagManager(tm);
		}

///    
///     <summary> * Tries to acquire read lock on <CODE>resource</CODE> for the current
///     * transaction. If read lock can't be acquired the transaction will wait for
///     * the lransaction until it can acquire it. If waiting leads to dead lock a
///     * <seealso cref="DeadlockDetectedException"/> will be thrown.
///     *  </summary>
///     * <param name="resource">
///     *            The resource </param>
///     * <exception cref="DeadlockDetectedException">
///     *             If a deadlock is detected </exception>
///     * <exception cref="IllegalResourceException"> </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void getReadLock(Object resource) throws DeadlockDetectedException, IllegalResourceException
		public virtual void getReadLock(object resource)
		{
			if (resource == null)
			{
				throw new IllegalResourceException("Null parameter");
			}

			RWLock @lock = null;
			@lock (resourceLockMap)
			{
				@lock = resourceLockMap.get(resource);
				if (@lock == null)
				{
					@lock = new RWLock(resource, ragManager);
					resourceLockMap.put(resource, @lock);
				}
				@lock.mark();
			}
			@lock.acquireReadLock();
		}

///    
///     <summary> * Tries to acquire write lock on <CODE>resource</CODE> for the current
///     * transaction. If write lock can't be acquired the transaction will wait
///     * for the lock until it can acquire it. If waiting leads to dead lock a
///     * <seealso cref="DeadlockDetectedException"/> will be thrown.
///     *  </summary>
///     * <param name="resource">
///     *            The resource </param>
///     * <exception cref="DeadlockDetectedException">
///     *             If a deadlock is detected </exception>
///     * <exception cref="IllegalResourceException"> </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void getWriteLock(Object resource) throws DeadlockDetectedException, IllegalResourceException
		public virtual void getWriteLock(object resource)
		{
			if (resource == null)
			{
				throw new IllegalResourceException("Null parameter");
			}

			RWLock @lock = null;
			@lock (resourceLockMap)
			{
				@lock = resourceLockMap.get(resource);
				if (@lock == null)
				{
					@lock = new RWLock(resource, ragManager);
					resourceLockMap.put(resource, @lock);
				}
				@lock.mark();
			}
			@lock.acquireWriteLock();
		}

///    
///     <summary> * Releases a read lock held by the current transaction on <CODE>resource</CODE>.
///     * If current transaction don't have read lock a
///     * <seealso cref="LockNotFoundException"/> will be thrown.
///     *  </summary>
///     * <param name="resource">
///     *            The resource </param>
///     * <exception cref="IllegalResourceException"> </exception>
///     * <exception cref="LockNotFoundException"> </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void releaseReadLock(Object resource) throws LockNotFoundException, IllegalResourceException
		public virtual void releaseReadLock(object resource)
		{
			if (resource == null)
			{
				throw new IllegalResourceException("Null parameter");
			}

			RWLock @lock = null;
			@lock (resourceLockMap)
			{
				@lock = resourceLockMap.get(resource);
				if (@lock == null)
				{
					throw new LockNotFoundException("Lock not found for: " + resource);
				}
				if (!@lock.isMarked() && @lock.getReadCount() == 1 && @lock.getWriteCount() == 0 && @lock.getWaitingThreadsCount() == 0)
				{
					resourceLockMap.Remove(resource);
				}
				@lock.releaseReadLock();
			}
		}

///    
///     <summary> * Releases a read lock held by the current transaction on <CODE>resource</CODE>.
///     * If current transaction don't have read lock a
///     * <seealso cref="LockNotFoundException"/> will be thrown.
///     *  </summary>
///     * <param name="resource">
///     *            The resource </param>
///     * <exception cref="IllegalResourceException"> </exception>
///     * <exception cref="LockNotFoundException"> </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void releaseWriteLock(Object resource) throws LockNotFoundException, IllegalResourceException
		public virtual void releaseWriteLock(object resource)
		{
			if (resource == null)
			{
				throw new IllegalResourceException("Null parameter");
			}

			RWLock @lock = null;
			@lock (resourceLockMap)
			{
				@lock = resourceLockMap.get(resource);
				if (@lock == null)
				{
					throw new LockNotFoundException("Lock not found for: " + resource);
				}
				if (!@lock.isMarked() && @lock.getReadCount() == 0 && @lock.getWriteCount() == 1 && @lock.getWaitingThreadsCount() == 0)
				{
					resourceLockMap.Remove(resource);
				}
				@lock.releaseWriteLock();
			}

		}

///    
///     <summary> * Utility method for debugging. Dumps info to console of txs having locks
///     * on resources.
///     *  </summary>
///     * <param name="resource"> </param>
///     
		public virtual void dumpLocksOnResource(object resource)
		{
			RWLock @lock = null;
			@lock (resourceLockMap)
			{
				if (!resourceLockMap.containsKey(resource))
				{
					Console.WriteLine("No locks on " + resource);
					return;
				}
				@lock = resourceLockMap.get(resource);
			}
			@lock.dumpStack();
		}

///    
///     <summary> * Utility method for debugging. Dumps the resource allocation graph to
///     * console. </summary>
///     
		public virtual void dumpRagStack()
		{
			ragManager.dumpStack();
		}

///    
///     <summary> * Utility method for debuggin. Dumps info about each lock to console. </summary>
///     
		public virtual void dumpAllLocks()
		{
			lock (resourceLockMap)
			{
				IEnumerator <RWLock> itr = resourceLockMap.values().GetEnumerator();
				int emptyLockCount = 0;
				while (itr.MoveNext())
				{
					RWLock @lock = itr.Current;
					if (@lock.getWriteCount() > 0 || @lock.getReadCount() > 0)
					{
						@lock.dumpStack();
					}
					else
					{
						if (@lock.getWaitingThreadsCount() > 0)
						{
							@lock.dumpStack();
						}
						emptyLockCount++;
					}
				}
				if (emptyLockCount > 0)
				{
					Console.WriteLine("There are " + emptyLockCount + " empty locks");
				}
				else
				{
					Console.WriteLine("There are no empty locks");
				}
			}
		}
	}
}